Chapter 1: Introduction

Once again, welcome back. In Lesson 2, I showed you how to use arrays. In Lesson 3, we saw how ArrayLists are often easier to use than arrays, and they provide similar features. You also learned that both of them are forms of Java collections, and we looked at some loop forms that are especially useful in working with collections.

While we're on the topic of collections, I want to talk a little more about Java's collections and how we can use them. At the same time, we'll learn more about Java's class hierarchy and how inheritance works in Java.

I hope you're ready to get started!

Chapter 2:
Class Hierarchy

Java is an object-oriented language. Part of what that means is that in Java, every new data type we create and every new program we write are classes. We have been using classes for quite a while now, so it's time we dig a little deeper into how class hierarchies work in Java.

When I say a class hierarchy, I'm referring to how every class in Java depends on, or inherits from, at least one other class. We call the class that inherits a subclass or a child class. The class that is inherited from is called a superclass, parent class, or base class.

TQA-11 --- Each of those classes, in turn, can depend on other classes above them in the hierarchy. Ultimately, every class traces its way back to Java's Object class. Every data type in Java except the primitive types (int, float, bool, and so on) inherits from the Object class.

What does it mean for a class to inherit from another class? It means that the child class contains all the characteristics, including the data fields and methods, of its parent. Since the parent also inherits from its parent, the bottom child class in a hierarchy inherits all the data and methods of all its ancestors above it in the hierarchy.

Before we get too far into this discussion and your eyes glaze over, let's look at a small example of a class hierarchy to help explain these concepts.

Suppose we want to set up a class hierarchy that describes some animals for an application that we could use in a biology class or at a zoo. First, we'll set up a base class named Animal. We'll put data and methods in that class that apply to all animals. For example, all animals move, and all animals eat. We'll put those actions in our Animal class and call them move() and eat(). (No surprises there!)

Let's also set up a diagram of our hierarchy so we have a visual representation to refer to. We'll use rectangles to represent our classes. Here's our Animal class:



A single class

Animal is too generic to represent all the animals we'll want to work with in our application, though. To take care of that, let's create some subclasses of the Animal class. As subclasses, they'll inherit the characteristics of their parent class, and then we'll add more specific data and methods that distinguish them from each other. For example, logical subclasses of Animal would be Vertebrate and Invertebrate. We could add the major characteristic that makes them different: an internal skeleton (or the lack thereof).

Under Vertebrate, we'll add more subclasses like Fish, Bird, and Mammal, with their unique characteristics: gills for Fish, feathers for Bird, and hair for Mammal. We'll add three more subclasses for Mammal just to fill out our example: Lion, Tiger, and Bear. (Oh, my!) Our example hierarchy looks like this:



A class hierarchy

Each subclass inherits all the characteristics (fields and methods) of its superclass, but it also has its own unique characteristics. For example, bears move differently than lions and tigers, since they sometimes walk on their hind feet. Bears hibernate; lions and tigers don't. Bears eat differently, too—they eat fruit and vegetables as well as meat. They are all mammals, though, and inherit all the Mammal characteristics.

Subclasses have another relationship with their parent classes that I call the "is a" relationship. In other words, an object of the subclass "is a" parent class object, too. A Bear is a Mammal, a Mammal is a Vertebrate, and so on. Whenever a program expects an object of type Mammal, an object of type Bear (or Lion or Tiger) will work, too, since they are Mammals.

By now you may be asking yourself, "What on earth does all this have to do with Java programming?" Let's talk about that.

The Java API Hierarchy

You probably already know about the Java API Specification. (If you don't, it's at Sun's Web site. See this lesson's Supplementary Material for a link.) The Java API Specification is a list of all the packages and classes that are part of Java. It also includes descriptions of their public fields and methods.

The connection to this lesson is that the API spells out the class hierarchy that comes with Java. It's a huge one! To get a better idea of how it works, we'll start with a quick and familiar example.

First, let's say we want to find out how to use the indexOf() method of the String class. We go to the API, scroll down until we see String in the lower-left pane, and click it. Documentation for the class will appear in the main pane.

We can immediately see that String is part of the java.lang package. (Its full name is java.lang.String.) We also see at the top of the pane that String inherits directly from the Object class. Just below that, we see its declaration:

public final class String
extends Object
implements Serializable, Comparable<String>, CharSequence
	

Under that are several paragraphs describing the class, followed by short descriptions of its field, its constructors (15 of them if I counted right), and its methods (more than 60 of them). Among the methods are the descriptions of the four versions of indexOf(). If I want more detail on one of them, I can click its name to jump to a longer description.

Are you getting a better idea of how class hierarchies work in Java? If you have any further questions about the String class hierarchy, let me know in the Discussion area.

Now, let's move on to our main topic!

Lesson 3:
ArrayList and Its Hierarchy

In Lesson 3, we used an ArrayList, and I've mentioned Java collections a couple of times. In the rest of this lesson, we'll tie them together and see how they relate in Java's class hierarchy. Then we'll build our own smaller version of the hierarchy to see how it's done.

To get started, let's look up ArrayList in the API. We'll look at two items there to understand its inheritance. First, we'll look at the classes it inherits from:



ArrayList class hierarchy


In addition to Object, it inherits from two other classes: AbstractList and AbstractCollection. While we're looking at what ArrayList inherits from them, we'll see what an abstract class is, too.

Second, we'll look at its declaration:

public class ArrayList<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, Serializable

Besides extending AbstractList, ArrayList also implements four interfaces: List<E>, RandomAccess, Cloneable, and Serializable. We'll focus on List<E> since it contains the properties we're interested in today. The others work the same way, so we'll ignore them in this lesson in the interest of time and space. In the next chapter, we'll talk about what the <E> means in these declarations, too.

ArrayList's Class Inheritance

We already know that ArrayList inherits from AbstractList and AbstractCollection. Their declarations in the API both contain a keyword we haven't seen before: abstract. When we look at their methods, we see that each of them has at least one method with that keyword. Any class with an abstract method is an abstract class.

TQA-10, 12 --- The abstract keyword in a method declaration tells Java that we're going to declare this method, but we're not going to implement it yet. In other words, we're going to tell Java what the method's name is, what its parameter list looks like, and what type of value it will return, but nothing else. That means the method won't work!

Since the method won't work, Java won't let us create objects from an abstract class. Every class we have created until now has been a concrete class, one that Java can create objects from. ArrayList is a concrete class, so we can create objects from it. What makes it different from AbstractList or AbstractCollection? And why would we want to create a class that can't be instantiated (can't create objects)?

Let's take a step back and talk in more general terms. Lists are something we use every day. We use shopping lists, to-do lists, guest lists, and other lists all the time. Java defines a list as an ordered collection. That just means a list is a bunch of things in a specific order, usually the order that we enter them.

In Java, there are several ways to create lists like that. (What those ways are is beyond the scope of this course. They use data structures we don't have time to cover.) When the creators of the Java class library set up the List mechanism, they didn't want to force programmers to use only one type of list. They also didn't want programmers who use lists to have to duplicate a lot of code to have different types of lists.

So they set up the AbstractList class. This class implements list methods that don't have to be changed for different list types. The class doesn't implement list methods that do have to change—instead, it leaves these methods abstract. Then, when someone makes a concrete list class (like ArrayList), it inherits all the common methods without having to rewrite them. It also inherits the abstract method with its name, parameter list, and return type. Before the class can be a concrete class, it must implement any abstract methods it inherits.

Getting back to specifics, AbstractList contains one abstract method: get(). Since ArrayList inherits it, the author of ArrayList had to write a get() method that does exactly what the author of AbstractList said it should do: get an item from the list. Since the author of ArrayList knows what structure it uses, he or she wrote an efficient get() method for that structure and gave us a concrete class that we can use for our lists.

ArrayList also inherits one abstract method from AbstractCollection: size(). ArrayList implements that method, too.

Now, if you'll bear with me through one more topic, we'll put all this to use.

Suppose I know what I want certain groups of classes (like lists) to do. Let's take it one step further than a class with one, or even a few, abstract methods. I know exactly what methods I want this group of classes to implement and what those methods should do. But since the different classes will implement them all differently, I won't define any of them yet; every one of them will be abstract.

Java has a special name for a completely abstract class. It's called an interface. The name comes from the meaning of the word interface, which describes how two entities interact. An interface in Java defines the way any concrete class that inherits from it will interact with other classes. Java interfaces are defined a little bit differently than classes are, and we'll look at the differences in just a minute. First, I want to reiterate the main idea of an interface: It defines a set of abstract methods that any class inheriting from it must implement.

We've been discussing two Java interfaces in this lesson without recognizing them as such. They are the List and Collection interfaces. Going back to the Java API, the AbstractCollection class implements the Collection interface. Likewise, the AbstractList class implements the List interface. Notice, too, that the List interface inherits from the Collection interface. That means that any concrete List class (like ArrayList) must implement all the methods defined in both interfaces.

Now let's bring it down to where "the rubber meets the road." What does this all mean in practice? If I want to create a concrete list class in Java that inherits from the List interface, there is already a group of defined methods that I have to implement in my class. The advantage is that anyone who uses my class can exchange it for any other concrete List class and use all the same method calls.

For example, Java has another List class named LinkedList. It is a very different type of list from ArrayList. For some processes, ArrayList is much faster. For other processes, LinkedList is faster. So I might want to use each of them in different situations. Since they both inherit from the List interface, they define the same methods to do the same list actions, even though those same methods are implemented differently in each case. But I can write a program using an ArrayList, then change it to use a LinkedList without changing any other code because they both implement all the List methods.

Pretty convenient, huh?

Chapter 4:
Using Interfaces
and Abstract Classes

Okay! We're finally through with generalities. Let's put all this into practice. We're going to create an interface, an abstract class, and a concrete class to do simple lists. That way we can see how they work together. After we create them, we're going to update our team roster project to use them so you can test them out.

So where do we start? Let's start with the interface, where we'll define what we want a list to do. And what do we normally do with lists? We add things to them, we count how big our list is, we scratch things off the list, and we look at specific items in the list. We may even be making a list for someone else (we parents do that, don't we?) and want to make a copy of it for others to see.

We'll put those operations into our lists. We'll even use the same names for them that the Java lists do: add, size, remove, get, and toString. We will define all these in our interface so anyone who wants to create a class from it will have to implement those methods.

Just to keep things straight, we will name our interface and classes differently so we don't confuse them with the Java lists. Let's name our interface MyList, our abstract class MyAbstractList, and our concrete class MyConcreteList. Here's the MyList interface:

    

public interface MyList<E> { /** * The MyList interface defines the list methods we'll use. */

/** * The add method will add an item to the end of the list. */ public void add(E element);

/** * The remove method will remove the item at the specified * location in the list, moving the following items one position to * the left. * * @param index the location at which to remove the element */ public void remove(int index);

/** * The get method will return the item at the specified * location in the list. * * @param index the location of the element to return */ public E get(int index);

/** * The size method will return the size of the list */ public int size();


/** * The toString method will return a string containing the contents * of the list. */ public String toString(); }

I included the comments so anyone using it can see what each of the methods does. We did not implement any of the methods; we just defined how to call them. The semicolon after each method signature ends it without an implementation.

You might be thinking that the one strange-looking part of this is the use of <E> and E in the interface. What's that all about?

Putting <E> after the interface name tells Java that this interface is going to be a generic one. In Java, the word generic has a very specific meaning. It means we're going to define a structure to hold data, but we don't yet know what type of data we're going to hold there! E (the specific letter doesn't matter, as long as we use the same one throughout) is just a placeholder. The actual type will be filled in when we pick a concrete class.

Lists can hold all kinds of things. I mentioned three earlier: shopping items, tasks to do, and guests. In Java, we can have lists of integers, Strings, floating point numbers, Players—anything you can imagine. We don't want to have to build different classes for each one, so Java has these generic classes that let us put any type of data in them. We used one for our player lists in earlier lessons, the ArrayList. Remember how we declared it? We said, ArrayList<Player>. We were telling Java that we wanted to use a generic structure to hold Player objects.

In our interface definition, every time we use the placeholder E, Java will replace the E with the actual type once we tell it what it is. Our abstract and concrete classes will work the same way.

That's all there is to an interface! On to the abstract class!

The abstract class implements the methods that don't need to change, no matter how the list is implemented. The add(), remove(), and get() methods depend on the final list implementation, so they need to be defined as abstract, but not implemented, in the abstract class. The size() and toString() methods can be implemented in the abstract class, though, since we can write them independently, like this:

    

public abstract class MyAbstractList<E> implements MyList<E> { protected int listSize = 0; public abstract void add(E element);

public abstract void remove(int index);

public abstract E get(int index);

public int size() { return listSize; }

public String toString() { String toReturn = "";

for (int i = 0; i < listSize; i++) { toReturn += this.get(i).toString(); }


return toReturn; } }

The abstract class includes an instance variable, listSize, that will contain the size of the list. The add() and remove() methods in the concrete class will update this field. But size() can go ahead and use it to return the size of the list in this class, and toString() can use it to control the loop it uses to create the output String.

Notice that even though the get() method is abstract, toString can call it here as if it were already defined. That's because Java will ensure that it gets defined before it is ever actually called.

Now we're ready to define a concrete class we can use. It needs to define the three remaining abstract methods—add(), remove(), and get()—and we'll do that like this:

    

public class MyConcreteList<E> extends MyAbstractList<E> implements MyList<E> { private Object[] myList;

public MyConcreteList() { myList = new Object[100]; }

public MyConcreteList(int size) { myList = new Object[size]; }

public void add(E element) {
myList[listSize] = element; listSize++; }

public void remove(int index) { listSize--; for (int i = index; i < listSize; i++) { myList[i] = myList[i+1]; } }


public E get(int index) { return (E) myList[index]; } }

This concrete class uses an array to store the list. The default constructor creates a list with a maximum size of 100. The other constructor takes an argument that allows a user to define a different size list. The add() method puts a new element in the list and increments listSize. The remove() method deletes the list element at the given location by moving the next element into its place, then moving the element after that up one spot, and so on to the end of the list. When it's done, all the elements that followed the deleted one have been moved one location, writing over the deleted one.

For example, if we have this list of characters:



A list of characters

And we call remove with an index of 3, the array will then look like this:



The list after removing 'W'

We deleted W by copying the K, then the M replaced the K and the H replaced the M. Since we also reduced the list's size by one, to six, the final H will never be seen, so it won't matter if it's still there. The next character we add to the list will replace it.

Finally, the get() method lets a user get one of the elements out of the array if she or he needs to work with it.

Just one more thing about this implementation. We used an array in the class, but it was an array of Objects, not of type E. That's because Java doesn't allow arrays of generic types. Since every type we can create inherits from Object, we can use an array of Objects to hold anything. (Remember the "is a" relationship in our class hierarchy?) Since the array can hold anything, we have to do one more thing in the get() method that may have caught your eye already. We have to tell Java what type of item we are getting out of the array, since it could hold any type at all. That is what the (E) does in the return statement of the get() method. It tells Java that the item we are getting out of the array is of type E. Java calls this casting.

You've heard of typecasting in the entertainment industry, right? Well, this is how typecasting works in the programming world. It tells Java to take whatever type it may have and convert it into the type we say it is. If Java can't make that conversion, it will let us know.

Now, the next step is to actually use the list class for something. I am going to make that your assignment for this lesson, so we won't do it here. I'll explain what you need to do in the assignment.

I have one more item to show you, then we'll be through with today's lesson. We're going to look at a different implementation of the concrete list that works just the same from a user's standpoint, but it's different internally. And it's based on the same interface and abstract class. We're going to implement the list using an ArrayList instead of an array. Here is the second concrete class that inherits from MyList and from MyAbstractList:

    

import java.util.ArrayList;

public class MyConcreteArrayList<E> extends MyAbstractList<E> implements MyList<E> { private ArrayList<E> myList;

public MyConcreteArrayList() { myList = new ArrayList<E>(100); }

public MyConcreteArrayList(int size) { myList = new ArrayList<E>(size); }

public void add(E element) { myList.add(element); listSize++; }

public void remove(int index) { listSize--; myList.remove(index); }

public E get(int index) { return myList.get(index); } }

Take a look at this class. It implements exactly the same methods as the first one and should work in someone else's program in just the same way. Other than the name, a programmer using these two classes could not tell the difference. But this implementation looks quite a bit different from the first one if you look at the internals.

You might be wondering why anyone would go to all that trouble to write two different classes that work the same way externally. The reason is internal performance. Remember how I mentioned a concrete Java class named LinkedList that behaves very much like ArrayList? For small applications, it would not make much difference which one you used. But for large applications, using the wrong one could mean a very slow process, and using the right one could mean a much faster, more successful process.

These complex structures have important uses.

Chapter 5: Summary

It may not seem like it, but we've rushed through the topic of inheritance, interfaces, and abstract classes very quickly. We have learned the basics about using them, and there's still a lot more to learn about them. But we'll save that for another course.

We've learned the basic essentials in this lesson:

Next time, we'll start down an entirely different path. We've been learning how to do a number of things in Java, but we've done everything in text-based console applications. In the next lesson, we'll start learning how to use Java to write stand-alone desktop applications using a graphical user interface, usually called a GUI. That means we'll be using windows, menus, the mouse, and all that fun stuff!

Don't forget to do the assignment. I wouldn't want you to miss the fun of actually using the two concrete classes we developed in this lesson.

If you have any questions, please let me know in the Discussion Area.

Until next time, then.

Lesson 4 FAQs

Q: Are interfaces and abstract classes widely used?

A: Yes, they are used very often. If you look at the Java API, every class name in the lower-left pane whose name is in italics is an interface. Every class whose name starts with Abstract is an abstract class. Many (probably most) large Java projects use one or both of these Java structures.

Lesson 4 Assignment

For this lesson's assignment, take the two concrete classes we created and make the team roster project work with both of them. I chose the team roster that wrote an output file, but any of the team roster projects will work using either of these lists in place of the arrays or ArrayLists we used in Lessons 2 and 3. The only change I had to make was to the Team class, since that is the class that defines the team list. My solutions are below if you want to look at them, but try to work it out on your own first. Let me know in the Discussion Area if you have any problems.

Here is my solution using the concrete class MyConcreteList:

Solution using MyConcreteList


And here's my solution using MyConcreteArrayList:

Solution using MyConcreteArrayList

Lesson 4 Quiz Answers

1. What is an abstract class?
A class with methods that are not implemented.

2. What is an interface?
A collection of related methods that a class can implement.

3. What is a subclass?
A class that inherits characteristics from another.

4. What is a concrete class?
A class with no abstract methods.

5. Which of the following is a logical use of interfaces, abstract classes, and concrete classes?
A concrete class inherits from an abstract class, which inherits from an interface.

Lesson 4 Supplementary Materials>